package com.boot.utils;

import com.boot.annotation.LogCompar;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @Describe 添加变更记录工具类
 * @Author xlma
 * @Date 2023/2/6 21:32
 * @Version 1.0
 */
@Slf4j
public class RecordUtils {

    /**
     * 注解列表(添加了@LogCompar的属性集合，数组的第一位是属性Field，第二位是注解LogCompar)
     */
    private static List<Object[]> fields;

    /**
     * 比较变更前后的数据，记录下添加了@LogCompar注解的属性，由原来的什么值变为了现在的什么值
     *
     * @param oldObj 原数据对象
     * @param newObj 新数据对象
     */
    public static String addChangeRecord(Object oldObj, Object newObj) {
        try {
            // 获取Class对象
            Class<?> oldClazz = oldObj.getClass();
            Class<?> newClazz = newObj.getClass();
            if (!oldClazz.equals(newClazz)) {
                throw new RuntimeException("请传入两个相同的实体类对象！");
            }
            // 获取加了注解的属性集合
            fields = getFields(oldClazz);
            if (fields.size() == 0) {
                throw new RuntimeException("没有找到要对比的属性！");
            }
            // 定义变量：变更描述信息
            StringBuilder info = new StringBuilder();
            // 根据注解列表，循环比较属性是否变化
            for (Object[] os : fields) {
                Field field = (Field) os[0];
                LogCompar logCompar = (LogCompar) os[1];
                // 获取新旧的属性值
                Object oldValue = field.get(oldObj);
                Object newValue = field.get(newObj);
                if (oldValue == null) {
                    oldValue = logCompar.defaultValue();
                }
                if (newValue == null) {
                    newValue = logCompar.defaultValue();
                }
                // 若新值和旧值相等，则跳过
                if (oldValue == newValue || newValue.equals(oldValue)) {
                    continue;
                }
                // 若是BigDecimal类型，需要compareTo方法判断是否相等,相等则跳过
                if (field.getType() == BigDecimal.class && ((BigDecimal) newValue).compareTo((BigDecimal) oldValue) == 0) {
                    continue;
                }
                // 若属性值有变化，则判断是否有码值
                if (StringUtils.isNotBlank(logCompar.readConverterExp())) {
                    oldValue = convertByExp(oldValue.toString(), logCompar.readConverterExp(), logCompar.separator());
                    newValue = convertByExp(newValue.toString(), logCompar.readConverterExp(), logCompar.separator());
                }
                // 自定义日期格式
                if (StringUtils.isNotBlank(logCompar.dateFormat())) {
                    oldValue = formatDate(oldValue, logCompar.dateFormat());
                    newValue = formatDate(newValue, logCompar.dateFormat());
                }
                info.append(String.format("%s-%s:由%s 变为了 %s;", field.getName(),logCompar.value(), oldValue, newValue));
            }
            // 此处可以返回info或直接保存日志表处理
            return info.toString();
        } catch (IllegalAccessException e) {
            log.error(e.getMessage());
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 获取注解属性信息
     */
    private static List<Object[]> getFields(Class<?> clazz) {
        // 定义加了@LogCompar注解的属性集合
        List<Object[]> fields = new ArrayList<Object[]>();
        // 定义临时的属性集合，包含了当前类及其父类的所有属性
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
        // 遍历临时属性集合，判断遍历出的属性上是否有@LogCompar注解，若有，则将当前属性及注解添加到fields中
        for (Field field : tempFields) {
            // 若当前属性field上添加了@LogCompar注解，则isAnnotationPresent()方法返回true
            if (field.isAnnotationPresent(LogCompar.class)) {
                LogCompar logCompar = field.getAnnotation(LogCompar.class);
                if (logCompar != null) {
                    // 设置可以访问私有private的属性
                    field.setAccessible(true);
                    fields.add(new Object[]{field, logCompar});
                }
            }
        }
        return fields;
    }

    /**
     * 解析导出值 0=男,1=女,2=未知
     *
     * @param propertyValue 参数值
     * @param converterExp  翻译注解
     * @param separator     分隔符
     * @return 解析后值
     */
    private static String convertByExp(String propertyValue, String converterExp, String separator) {
        StringBuilder stringBuilder = new StringBuilder();
        String[] convertSource = converterExp.split(separator);
        for (String item : convertSource) {
            String[] itemArray = item.split("=");
            if (StringUtils.containsAny(separator, propertyValue)) {
                for (String value : propertyValue.split(separator)) {
                    if (itemArray[0].equals(value)) {
                        stringBuilder.append(itemArray[1] + separator);
                        break;
                    }
                }
            } else if (itemArray[0].equals(propertyValue)) {
                return itemArray[1];
            }
        }
        return StringUtils.stripEnd(stringBuilder.toString(), separator);
    }


    /**
     * 格式化日期显示格式
     *
     * @param localDateTime
     * @param pattern
     * @return
     */
    private static String formatDate(Object localDateTime, String pattern) {
        LocalDateTime dateConvert = (LocalDateTime) localDateTime;
        DateTimeFormatter formatter = null;
        if (StringUtils.isBlank(pattern)) {
            formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        }
        if (!StringUtils.isBlank(pattern)) {
            formatter = DateTimeFormatter.ofPattern(pattern);
        }
        String dateTimeStr = dateConvert.format(formatter);
        return dateTimeStr;
    }


}
